using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace TankWars.Weapons
{
    public class WeaponManager
    {
        public enum WeaponCategory {Ammo, Energy, Infinite};

        private Tank tank;

        private Dictionary<Type, Texture2D> dicShotIcons = new Dictionary<Type, Texture2D>();
        private Dictionary<Type, Texture2D> dicShotTextures = new Dictionary<Type, Texture2D>();

        private Type tyPrimaryShotType;
        private Type tyDefaultPrimaryShotType = typeof(Missile);
        private Type tySecondaryShotType;
        private Type tyDefaultSecondaryShotType = typeof(Mine);

        private bool bPrimaryReadyToFire = true;
        private double dPrimaryLastTimeFired = 0;
        private double dPrimaryTimeBetweenFiring = 0;

        private bool bSecondaryReadyToFire = true;
        private double dSecondaryLastTimeFired = 0;
        private double dSecondaryTimeBetweenFiring = 0;

        private List<Shot> lstPrimaryShotsFired = new List<Shot>();
        private List<Shot> lstSecondaryShotsFired = new List<Shot>();

        private WeaponCategory PrimaryWeaponAmmoType = WeaponCategory.Infinite;
        private WeaponCategory SecondaryWeaponAmmoType = WeaponCategory.Infinite;
        private int nPrimaryAmmo = 0;
        private int nPrimaryEnergy = 0;
        private int nPrimaryChargeCostPerFire = 0;
        private int nSecondaryAmmo = 0;
        private int nSecondaryEngery = 0;
        private int nSecondaryChargeCostPerFire = 0;

        #region Properties
        /// <summary>
        /// Gets and sets the type of primary shot the tank fires.
        /// </summary>
        public Type PrimaryShotType
        {
            get
            {
                return this.tyPrimaryShotType;
            }
            set
            {
                //The the given type is a shot then it's ok, otherwise keep the current shot type
                if (value.BaseType == typeof(Shot))
                {
                    this.tyPrimaryShotType = value;
                }

                //Get the time between firing for the given shot
                double dTempTimeBetweenFiring = 0;
                System.Reflection.FieldInfo fieldInfo = this.tyPrimaryShotType.GetField("N_TIME_BETWEEN_FIRING");
                if (fieldInfo != null)
                {
                    dTempTimeBetweenFiring = (int)fieldInfo.GetValue(null);
                }

                this.dPrimaryTimeBetweenFiring = dTempTimeBetweenFiring;

                fieldInfo = this.tyPrimaryShotType.GetField("SHOT_CATEGORY");
                if (fieldInfo != null)
                {
                    this.PrimaryWeaponAmmoType = (WeaponCategory)fieldInfo.GetValue(null);
                }

                if (this.PrimaryWeaponAmmoType == WeaponCategory.Ammo)
                {
                    fieldInfo = this.tyPrimaryShotType.GetField("N_MAX_AMMO");
                    this.nPrimaryAmmo = (int)fieldInfo.GetValue(null);
                }
                else if (this.PrimaryWeaponAmmoType == WeaponCategory.Energy)
                {
                    fieldInfo = this.tyPrimaryShotType.GetField("N_CHARGE_COST_PER_FIRE");
                    this.nPrimaryChargeCostPerFire = (int)fieldInfo.GetValue(null);

                    this.nPrimaryEnergy = 100;
                }
                else
                {
                    this.nPrimaryAmmo = 0;
                    this.nPrimaryEnergy = 0;
                    this.nPrimaryChargeCostPerFire = 0;
                }   
            }
        }
        /// <summary>
        /// Gets and sets the type of secondary shot the tank fires.
        /// </summary>
        public Type SecondaryShotType
        {
            get
            {
                return this.tySecondaryShotType;
            }
            set
            {
                //The the given type is a shot then it's ok, otherwise keep the current shot type
                if (value.BaseType == typeof(Shot))
                {
                    this.tySecondaryShotType = value;
                }

                //Get the time between firing for the given shot
                double dTempTimeBetweenFiring = 0;
                System.Reflection.FieldInfo fieldInfo = this.tySecondaryShotType.GetField("N_TIME_BETWEEN_FIRING");
                if (fieldInfo != null)
                {
                    dTempTimeBetweenFiring = (int)fieldInfo.GetValue(null);
                }

                this.dSecondaryTimeBetweenFiring = dTempTimeBetweenFiring;

                fieldInfo = this.tySecondaryShotType.GetField("SHOT_CATEGORY");
                if (fieldInfo != null)
                {
                    this.SecondaryWeaponAmmoType = (WeaponCategory)fieldInfo.GetValue(null);
                }

                if (this.SecondaryWeaponAmmoType == WeaponCategory.Ammo)
                {
                    fieldInfo = this.tySecondaryShotType.GetField("N_MAX_AMMO");
                    this.nSecondaryAmmo = (int)fieldInfo.GetValue(null);
                }
                else if (this.SecondaryWeaponAmmoType == WeaponCategory.Energy)
                {
                    fieldInfo = this.tySecondaryShotType.GetField("N_CHARGE_COST_PER_FIRE");
                    this.nSecondaryChargeCostPerFire = (int)fieldInfo.GetValue(null);

                    this.nSecondaryEngery = 100;
                }
                else
                {
                    this.nSecondaryAmmo = 0;
                    this.nSecondaryEngery = 0;
                    this.nSecondaryChargeCostPerFire = 0;
                }
            }
        }
        /// <summary>
        /// Gets the default shot type of the primary weapon
        /// </summary>
        public Type DefaultPrimaryShotType
        {
            get
            {
                return this.tyDefaultPrimaryShotType;
            }
        }
        /// <summary>
        /// Gets the default shot type of the secondary weapon
        /// </summary>
        public Type DefaultSecondaryShotType
        {
            get
            {
                return this.tyDefaultSecondaryShotType;
            }
        }

        public WeaponCategory PrimaryWeaponCategory
        {
            get
            {
                return this.PrimaryWeaponAmmoType;
            }
        }
        public WeaponCategory SecondaryWeaponCategory
        {
            get
            {
                return this.SecondaryWeaponAmmoType;
            }
        }

        public int PrimaryAmmo
        {
            get
            {
                return this.nPrimaryAmmo;
            }
        }
        public int PrimaryEnergy
        {
            get
            {
                return this.nPrimaryEnergy;
            }
        }

        public int SecondaryAmmo
        {
            get
            {
                return this.nSecondaryAmmo;
            }
        }
        public int SecondaryEnergy
        {
            get
            {
                return this.nSecondaryEngery;
            }
        }

        public Dictionary<Type, Texture2D> ShotIcons
        {
            get
            {
                return this.dicShotIcons;
            }
        }
        #endregion

        /// <summary>
        /// Creates a new weapon manager for the given tank
        /// </summary>
        /// <param name="tank">Tank that owns the weapon manager</param>
        /// <param name="ShotTextures">All Shot textures</param>
        /// <param name="ShotIcons">All Shot icons</param>
        public WeaponManager(Tank tank, Dictionary<Type, Texture2D> ShotTextures, Dictionary<Type, Texture2D> ShotIcons)
        {
            this.tank = tank;

            this.dicShotTextures = ShotTextures;
            this.dicShotIcons = ShotIcons;

            this.PrimaryShotType = this.tyDefaultPrimaryShotType;
            this.SecondaryShotType = this.tyDefaultSecondaryShotType;
        }

        /// <summary>
        /// Fires the primary weapon
        /// </summary>
        /// <param name="gameTime"></param>
        public void FirePrimary(GameTime gameTime)
        {
            if (this.bPrimaryReadyToFire)
            {
                Shot shotFired = (Shot)this.tyPrimaryShotType.GetConstructor(new Type[] { typeof(Tank), typeof(Texture2D) }).Invoke(new object[] { this.tank, this.dicShotTextures[this.tyPrimaryShotType] });
                this.lstPrimaryShotsFired.Add(shotFired);

                //Wait before firing again
                this.bPrimaryReadyToFire = false;
                this.dPrimaryLastTimeFired = gameTime.TotalGameTime.TotalMilliseconds;

                //Take off ammo or energy for shot
                if (this.PrimaryWeaponAmmoType == WeaponCategory.Ammo)
                {
                    //Take off one ammo
                    this.nPrimaryAmmo -= 1;
                }
                else if (this.PrimaryWeaponAmmoType == WeaponCategory.Energy)
                {
                    //Take off amount of charge for the weapon
                    this.nPrimaryEnergy -= this.nPrimaryChargeCostPerFire;

                    if (this.nPrimaryEnergy < 0)
                    {
                        this.nPrimaryEnergy = 0;
                    }
                }
                else
                {
                    //Infinite ammo
                }
            }

            //Check if ammo or energy left
            if (this.PrimaryWeaponAmmoType == WeaponCategory.Ammo)
            {
                //If no ammo left
                if (this.nPrimaryAmmo == 0)
                {
                    //Change weapon back to default
                    this.PrimaryShotType = this.tyDefaultPrimaryShotType;
                }
            }
            else if (this.PrimaryWeaponAmmoType == WeaponCategory.Energy)
            {
                //If no energy left
                if (this.nPrimaryEnergy == 0)
                {
                    //Change weapon back to default
                    this.PrimaryShotType = this.tyDefaultPrimaryShotType;
                }
            }
            else
            {
                //Infinite ammo
            }
        }
        /// <summary>
        /// Fires the secondary weapon
        /// </summary>
        /// <param name="gameTime"></param>
        public void FireSecondary(GameTime gameTime)
        {
            if (this.bSecondaryReadyToFire)
            {
                Shot shotFired = (Shot)this.tySecondaryShotType.GetConstructor(new Type[] { typeof(Tank), typeof(Texture2D) }).Invoke(new object[] { this.tank, this.dicShotTextures[this.tySecondaryShotType] });
                this.lstSecondaryShotsFired.Add(shotFired);

                //Wait before firing again
                this.bSecondaryReadyToFire = false;
                this.dSecondaryLastTimeFired = gameTime.TotalGameTime.TotalMilliseconds;

                //Remove ammo for shot
                if (this.SecondaryWeaponAmmoType == WeaponCategory.Ammo)
                {
                    //Take off one ammo
                    this.nSecondaryAmmo -= 1;
                }
                else if (this.SecondaryWeaponAmmoType == WeaponCategory.Energy)
                {
                    //Take off amount of charge for the weapon
                    this.nSecondaryEngery -= this.nSecondaryChargeCostPerFire;

                    if (this.nSecondaryEngery < 0)
                    {
                        this.nSecondaryEngery = 0;
                    }
                }
                else
                {
                    //Infinite ammo
                }
            }

            //Check if ammo or energy left
            if (this.SecondaryWeaponAmmoType == WeaponCategory.Ammo)
            {
                //If no ammo left
                if (this.nSecondaryAmmo == 0)
                {
                    //Change weapon back to default
                    this.SecondaryShotType = this.tyDefaultSecondaryShotType;
                }
            }
            else if (this.SecondaryWeaponAmmoType == WeaponCategory.Energy)
            {
                //If no energy left
                if (this.nSecondaryEngery == 0)
                {
                    //Change weapon back to default
                    this.SecondaryShotType = this.tyDefaultSecondaryShotType;
                }
            }
            else
            {
                //Infinite ammo
            }
        }

        /// <summary>
        /// Checks to see if primary and secondary weapons are ready to fire.
        /// Updates the position of all primary and secondary shots.
        /// </summary>
        /// <param name="gameTime"></param>
        public void Update(GameTime gameTime)
        {
            this.CheckReadyToFire(gameTime);

            foreach (Shot currentShot in this.lstPrimaryShotsFired)
            {
                currentShot.UpdatePosition(gameTime);
            }

            foreach (Shot currentShot in this.lstSecondaryShotsFired)
            {
                currentShot.UpdatePosition(gameTime);
            }
        }
        /// <summary>
        /// Checks to see if primary and secondary weapons are ready to fire again
        /// </summary>
        /// <param name="gameTime"></param>
        public void CheckReadyToFire(GameTime gameTime)
        {
            if (!this.bPrimaryReadyToFire)
            {
                if ((gameTime.TotalGameTime.TotalMilliseconds - this.dPrimaryLastTimeFired) > this.dPrimaryTimeBetweenFiring)
                {
                    this.bPrimaryReadyToFire = true;
                }
            }

            if (!this.bSecondaryReadyToFire)
            {
                if ((gameTime.TotalGameTime.TotalMilliseconds - this.dSecondaryLastTimeFired) > this.dSecondaryTimeBetweenFiring)
                {
                    this.bSecondaryReadyToFire = true;
                }
            }
        }

        public void Draw(SpriteBatch spriteBatch, Map map)
        {
            foreach (Shot currentShot in this.lstPrimaryShotsFired)
            {
                currentShot.Draw(spriteBatch, map);
            }

            foreach (Shot currentShot in this.lstSecondaryShotsFired)
            {
                currentShot.Draw(spriteBatch, map);
            }
        }
    }
}
